export class ExpressionClip {
    isExpression = false;
    str!: string;
    
    constructor (str: string, isExpression: boolean) {
        this.isExpression = isExpression;
        this.str = str;
    }
}

export default class Expression {
    clips: ExpressionClip[] = [];

    static executeScript(formula: string, params = {}) {
        const _$MR = params;
        return eval(formula);
    }


    /** 是否包含表达式 */
    static isIncludeExpression(str: string) {
        return /\$(.*)\%/.test(str) || /\$\((.*)\)\%/.test(str);
    }


    static parse(str: string, params: any = {}) {
        var ex = new Expression();
        Expression._parse1(str, ex.clips, params);
        return ex;
    }

    private static _parse1(str: string, clips: ExpressionClip[], params: any = {}) {
        var regExp1 = /\$\((.*?)\)\%/g;
        
        var lastIndex = 0;
        do {
            var res = regExp1.exec(str);
            if (res) {
                if (lastIndex != res.index) {
                    Expression._parse2(str.substring(lastIndex, res.index), clips, params)
                }
                //转换
                clips.push(new ExpressionClip(Expression.validAndConvert(res[1], params), true));
                lastIndex = res.index + res[0].length;
            }
            else if (lastIndex != str.length) {
                Expression._parse2(str.substring(lastIndex, str.length), clips, params)
            }
        } while (res && lastIndex != str.length);
    }

    private static _parse2(str: string, clips: ExpressionClip[], params: any = {}) {
        var regExp1 = /\$(.*?)\%/g;

        var lastIndex = 0;
        do {
            var res = regExp1.exec(str);
            if (res) {
                if (lastIndex != res.index) {
                    clips.push(new ExpressionClip(str.substring(lastIndex, res.index), false));
                }
                //转换
                clips.push(new ExpressionClip(Expression.validAndConvert(res[1], params), true));
                lastIndex = res.index + res[0].length;
            }
            else if (lastIndex != str.length) {
                clips.push(new ExpressionClip(str.substring(lastIndex, str.length), false));
            }
        } while (res && lastIndex != str.length);
    }


    static validAndConvert(formula: string, params: any = {}) {
        if ('' == formula) {
            throw new Error('无运算内容');
        }
        //去掉空格
        formula = formula.replace(new RegExp(' ', 'g'), '');
        //限定字符
        var r = /[^A-z0-9\*\(\)\-\+\^\!\=/]/.exec(formula);
        if (r) {
            throw new Error('包含非法字符' + r[0]);
        }
        //次幂
        formula = formula.replace('^', '**');
        //变量
        var a = /([A-Za-z][A-Za-z0-9]+)/gi;
        var res;
        while(res = a.exec(formula)) {
            if (!Object.hasOwn(params, res[1])) {
                throw new Error(`${res[1]}变量没有定义`);
            }
        }
        formula = formula.replace(a, '_$MR.$1');
        //避免$和_变量
        return formula;
    }


    eval(param: any = {}) : any {
        if(this.clips.length == 0) {
            return null;
        }
        else if(this.clips.length == 1){
            if(this.clips[0].isExpression){
                return Expression.executeScript(this.clips[0].str, param);
            }
            else {
                return this.clips[0];
            }
        }
        else {
            var str = "";
            for (let clip of this.clips) {
                if(clip.isExpression) {
                    str += Expression.executeScript(clip.str, param)
                }
                else {
                    str += clip.str;
                }
            }
            return str;
        }
    }
}